// X-Chat Bot Control stuff for help in #tigcc
// Portions (largely) lifted from Kevin Kofler's xctigcc plugin, as well as
// Peter Zelezný's X-Chat plugin examples (both GPL'd).
//
// See `license.txt' for a copy of the license (GPL).

#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <glib.h>
#include <time.h>
#include <stdint.h>
#include "xchat-plugin.h"

static xchat_plugin *ph;
static int command_cb(char *word[], void *userdata);
static int nickchange_cb(char *word[], void *userdata);
static int present(char *who);
static int authorize(const char *user);
static GHashTable *useridtab;
static GHashTable *iddatetab;
/*static GHashTable *idsaidtab;*/
static intptr_t uidcur = 1;

/**
 * Bot functionality:
 * !nick    - changes nickname
 * !load    - loads a plugin
 * !unload  - unloads a plugin
 * !stats   - display statistics about the machine.
 * !restart - restart bot
 * !reident - retake name, reidentify, reop
 * !version - print revision
 */

__declspec(dllexport)
int xchat_plugin_init(xchat_plugin *plugin_handle,
                      char **plugin_name,
                      char **plugin_desc,
                      char **plugin_version,
                      char **arg)
{
  ph = plugin_handle;

  *plugin_name = "X-Chat #tigcc Bot Controls for Remote Operation";
  *plugin_desc = "Allows for remote restarting, etc.";
  *plugin_version = "$Rev: 21 $";

  xchat_hook_print(ph, "Channel Message", XCHAT_PRI_NORM, command_cb, NULL);
  xchat_hook_print(ph, "Channel Msg Hilight", XCHAT_PRI_NORM, command_cb, NULL);
  xchat_hook_print(ph, "Private Message", XCHAT_PRI_NORM, command_cb, NULL);
  xchat_hook_print(ph, "Private Message to Dialog", XCHAT_PRI_NORM, command_cb, NULL);
  xchat_hook_print(ph, "Change Nick", XCHAT_PRI_NORM, nickchange_cb, NULL);

  useridtab = g_hash_table_new(g_str_hash, g_str_equal);
  iddatetab = g_hash_table_new(NULL, NULL);
  /*idsaidtab = g_hash_table_new(NULL, NULL);*/

  xchat_print(ph, "#tigcc BotCommand loaded successfully.\n");

  return 1;       /* successful */
}

static int nickchange_cb(char *word[], void *userdata)
{
  char *from = word[1];
  char *to   = word[2];
  intptr_t uid;
  time_t now = time(NULL);

  if (!(uid = (intptr_t)g_hash_table_lookup(useridtab, from)))
    // if the user doesn't exist
    uid = uidcur++;
  else
    g_hash_table_remove(useridtab, from);

  g_hash_table_insert(useridtab, strdup(to), (gpointer)uid);
  g_hash_table_replace(iddatetab, (gpointer)uid, (gpointer)now);
  /*g_hash_table_replace(idsaidtab, (gpointer)uid, strdup("<Nick Changed>"));*/

  return XCHAT_EAT_NONE;
}

static void heard(char *word[])
{
  intptr_t uid;
  if (!(uid = (intptr_t)g_hash_table_lookup(useridtab, word[1])))
  {
    uid = uidcur++;
    g_hash_table_insert(useridtab, strdup(word[1]), (gpointer)uid);
  }

  g_hash_table_replace(iddatetab, (gpointer)uid, (gpointer)time(NULL));
  /*g_hash_table_replace(idsaidtab, uid, strdup(word[2]));*/
}

static int command_cb(char *word[], void *userdata)
{
  const char *secondarg;
  char output[81];
  FILE *stream;

  heard(word);

  // allow ops to change the nick of the bot
  if (!strncmp(word[2], "!nick", 5)) {
    if (!strncmp(word[2], "!nick ", 6) && (strlen(word[2]) > 6) &&
        (strlen(word[2]) < 21) && authorize(word[1])) {
      secondarg = word[2] + 6;
      xchat_commandf(ph, "NICK %s", secondarg);
    }
    else if (*(word[2] + 5) == '\0' && authorize(word[1]))
      xchat_commandf(ph, "NICK tigcc_bot");
  }
  // allow ops to load plugins located on local filesystem... no
  // remote loading yet, maybe direct bot to ftp, http, or dcc?
  else if (!strncmp(word[2], "!load", 5)) {       // potentially harmful
    if (!strncmp(word[2], "!load ", 6) && (strlen(word[2]) > 6) &&
        authorize(word[1])) {
      secondarg = word[2] + 6;
      xchat_commandf(ph, "LOAD %s", secondarg);
    }
    else if (authorize(word[1]))
      xchat_commandf(ph, "MSG #tigcc Load what?");
  }
  // unload.
  else if (!strncmp(word[2], "!unload", 7)) {
    if (!strncmp(word[2], "!unload ", 8) && (strlen(word[2]) > 8) &&
        authorize(word[1])) {
      secondarg = word[2] + 8;
      xchat_commandf(ph, "UNLOAD %s", secondarg);
    }
    else if (authorize(word[1]))
      xchat_commandf(ph, "MSG #tigcc Unload what?");
  }
  // restart.
  else if (!strcmp(word[2], "!restart") && authorize(word[1])) {
    system("/home/konrad/daemonize /home/konrad/bot_self_restart");
    xchat_commandf(ph, "QUIT Restarting... [Requested by %s]", word[1]);
    xchat_command(ph, "CLOSE");
  }
  // stats
  else if (!strcmp(word[2], "!stats")
//    && authorize(word[1])   // we don't need to authorize for this
      ) {
    xchat_command(ph, "MEMINFO");
    if((stream = popen("uptime", "r"))) {
      fgets((char *)output, 81, stream);
      pclose(stream);
      xchat_commandf(ph, "MSG #tigcc %s", (char *)output);
    }
  }
  // reidentify
  else if (!strcmp(word[2], "!reident") && authorize(word[1])) {
    stream = fopen("/home/konrad/.xchat2/servlist_.conf", "r");
    for(;;) {
      fgets((char *)output, 81, stream);
      if(output[0] == 'B') break;
    }
    fclose(stream);
    xchat_commandf(ph, "MSG nickserv ghost tigcc_bot %s", ((char *)output) + 2);
    xchat_command(ph, "NICK tigcc_bot");
    xchat_commandf(ph, "MSG nickserv identify %s", ((char *)output) + 2);
    xchat_command(ph, "MSG chanserv op #tigcc");
  }
  else if (!strcmp(word[2], "!version")) {
    xchat_command(ph, "MSG #tigcc $Rev: 21 $");
  }
  else if (!strncmp(word[2], "!seen", 5)) {
    if (strlen(word[2]) < 7)
      xchat_command(ph, "MSG #tigcc Seen who?");
    else
    {
      char *who = word[2] + 6;
      if (present(who))
        xchat_commandf(ph, "MSG #tigcc %s is already here!", who);
      else
      {
        intptr_t uid = (intptr_t)g_hash_table_lookup(useridtab, who);
        if (!uid) xchat_commandf(ph, "MSG #tigcc I have never seen %s.", who);
        else
        {
          time_t seen = (time_t)g_hash_table_lookup(iddatetab, (gpointer)uid),
                 now = time(NULL);
          if (!seen)
            xchat_commandf(ph, "MSG #tigcc Internal error: uid (%ld) not found in"
                " date table (who: %s).", uid, who);
          else
            xchat_commandf(ph, "MSG #tigcc %s was last seen %ld seconds ago.", who, now - seen);
        }
      }
    }
  }
  return XCHAT_EAT_NONE;
}

static int present(char *who)
{
  xchat_list *list;
  int present = 0;

  if (list = xchat_list_get(ph, "users")) {
    while (xchat_list_next(ph, list)) {
      if (!xchat_nickcmp(ph, xchat_list_str(ph, list, "nick"), who))
      {
        present = 1;
        break;
      }
    }
    xchat_list_free(ph, list);
  }
  else
    xchat_command(ph, "MSG #tigcc Error: userlist could not be accessed for some reason.");
  return present;
}

static int authorize(const char *nick)
{
  xchat_list *list;
  int authorized = 0;

  if (list = xchat_list_get(ph, "users")) {
    while (xchat_list_next(ph, list)) {
      if (*(xchat_list_str(ph, list, "prefix")) == '@' &&
            !xchat_nickcmp(ph, xchat_list_str(ph, list, "nick"), nick)) {
        authorized = 1;
        break;
      }
    }
    xchat_list_free(ph, list);
  }
  else
    xchat_command(ph, "MSG #tigcc Userlist could not be accessed for some reason [Access Denied]");
  return authorized;
}
